package com.hardson.toolkit.util;

import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileExistsException;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.IOFileFilter;

import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class FileUtils {

    private static final LookAndFeel oriLookAndFeel = UIManager.getLookAndFeel();

    private static Image defaultSwingIconImage;

    public static void setDefaultSwingIcon(Image iconImage) {
        defaultSwingIconImage = iconImage;
    }

    public static File chooseDirFile(Component parent, String oldDir) {
        setSystemLookAndFeel();
        JFileChooser ch = new HardsonFileChooser(defaultSwingIconImage);
        ch.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        if (!StrUtil.isTrimEmpty(oldDir)) {
            ch.setCurrentDirectory(new File(oldDir));
        }
        int flag = ch.showOpenDialog(parent);
        setOriLookAndFeel();
        if (flag == JFileChooser.APPROVE_OPTION) {
            return ch.getSelectedFile();
        }
        return null;
    }

    public static String chooseDirPath(Component parent, String oldDir) {
        File file = chooseDirFile(parent, oldDir);
        return file == null ? "" : file.getAbsolutePath();
    }

    public static String chooseFile(Component parent, String oldDir, FileFilter[] filter,
                                    int dialogType) {
        setSystemLookAndFeel();
        JFileChooser ch = new HardsonFileChooser(defaultSwingIconImage);
        ch.setFont(new Font("宋体", java.awt.Font.PLAIN, 12));
        ch.setDialogType(dialogType);
        ch.setFileSelectionMode(JFileChooser.FILES_ONLY);
        if (filter != null) {
            for (int i = filter.length; --i >= 0; ) {
                ch.addChoosableFileFilter(filter[i]);
            }
        }
        if (!StrUtil.isTrimEmpty(oldDir)) {
            ch.setCurrentDirectory(new File(oldDir));
        }
        ch.setAcceptAllFileFilterUsed(false);
        ch.setAcceptAllFileFilterUsed(true);// 通过移除、再添加，将“所有文件”选项放到后面
        int flag = ch.showDialog(parent, null);
        setOriLookAndFeel();
        if (flag == JFileChooser.APPROVE_OPTION) {
            String file = ch.getSelectedFile().getAbsolutePath();
            if (ch.getDialogType() == JFileChooser.SAVE_DIALOG) {
                if (file.lastIndexOf(".") == -1) {
                    if (ch.getFileFilter() instanceof FileNameExtensionFilter) {
                        file =
                                file + "." +
                                        ((FileNameExtensionFilter) ch.getFileFilter()).getExtensions()[0];
                    } else {
                        file = file + ".txt";
                    }
                }
            }
            // PropertiesUtil.setProperty(PropertiesUtil.KEY_RECENTDIR,new
            // File(file).getParent());
            return file;
        }
        return "";
    }

    public static String chooseFile(Component parent, String oldDir, String description,
                                    String... extensions) {
        File file = chooseFileFile(parent, oldDir, description, extensions);
        return file == null ? "" : file.getAbsolutePath();
    }

    public static File chooseFileFile(Component parent, String oldDir, String description,
                                      String... extensions) {
        setSystemLookAndFeel();
        JFileChooser ch = new HardsonFileChooser(defaultSwingIconImage);
        if (!ArrayUtil.isEmpty(extensions)) {
            FileNameExtensionFilter filter = new FileNameExtensionFilter(description, extensions);
            ch.setFileFilter(filter);
        }
        ch.setFileSelectionMode(JFileChooser.FILES_ONLY);
        if (!StrUtil.isTrimEmpty(oldDir)) {
            ch.setCurrentDirectory(new File(oldDir));
        }
        int op = ch.showOpenDialog(parent);
        setOriLookAndFeel();
        if (op == JFileChooser.APPROVE_OPTION) {
            return ch.getSelectedFile();
        }
        return null;
    }

    public static File[] chooseMutiFile(Component parent, String oldDir,
                                        String description,
                                        String... extensions) {
        setSystemLookAndFeel();
        JFileChooser ch = new HardsonFileChooser(defaultSwingIconImage);
        if (!ArrayUtil.isEmpty(extensions)) {
            FileNameExtensionFilter filter = new FileNameExtensionFilter(description, extensions);
            ch.setFileFilter(filter);
        }
        ch.setFileSelectionMode(JFileChooser.FILES_ONLY);
        ch.setMultiSelectionEnabled(true);
        if (!StrUtil.isTrimEmpty(oldDir)) {
            ch.setCurrentDirectory(new File(oldDir));
        }
        int op = ch.showOpenDialog(parent);
        setOriLookAndFeel();
        if (op == JFileChooser.APPROVE_OPTION) {
            return ch.getSelectedFiles();
        }
        return null;
    }

    public static String chooseFile(Component parent, String oldDir,
                                    javax.swing.filechooser.FileFilter filter) {
        setSystemLookAndFeel();
        JFileChooser ch = new HardsonFileChooser(defaultSwingIconImage);
        ch.setFileSelectionMode(JFileChooser.FILES_ONLY);
        if (filter != null) {
            ch.setFileFilter(filter);
        }
        if (!StrUtil.isTrimEmpty(oldDir)) {
            ch.setCurrentDirectory(new File(oldDir));
        }
        int flag = ch.showOpenDialog(parent);
        setOriLookAndFeel();
        if (flag == JFileChooser.APPROVE_OPTION) {
            return ch.getSelectedFile().getAbsolutePath();
        }
        return "";
    }

    private static void setSystemLookAndFeel() {
        try {
            String osName = System.getProperty("os.name").toLowerCase();
            if (osName.contains("windows")) {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static void setOriLookAndFeel() {
        try {
            UIManager.setLookAndFeel(oriLookAndFeel);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 获取用户工作路径（程序启动路径）
     *
     * @return String 用户工作路径
     */
    public static String getUserDir() {
        return System.getProperty("user.dir");
    }

    /**
     * 获取用户工作路径（程序启动路径），拼接相对路径
     *
     * @return String 用户工作路径拼接相对路径后的路径
     */
    public static String getUserDir(String... relativeName) {
        String filePath = getUserDir();
        String defPath = String.join(File.separator, relativeName);
        return String.join(File.separator, filePath, defPath);
    }
    /**
     * 获取启动系统应用的操作系统用户的家目录 如Linux操作系统:/home/XiaoMing;Windows操作系统:C:\Users\Administrator
     *
     * @return String 用户家目录
     */
    public static String getHomeDir() {
        return System.getProperty("user.home");
    }

    /**
     * 获取启动系统应用的操作系统用户的家目录，并拼接指定的相对路径
     *
     * @param relativeName:
     * @return String 用户家目录拼接相对路径后的路径
     * @author hardson
     * @date 2025/3/22 上午11:53
     */
    public static String getHomeDir(String... relativeName) {
        String homePath = getHomeDir();
        String defPath = String.join(File.separator, relativeName);
        return String.join(File.separator, homePath, defPath);
    }

    /**
     * 获取启动系统应用的操作系统用户的家目录，并拼接指定的相对路径
     *
     * @param relativeName:
     * @return String 用户家目录拼接相对路径后的路径
     * @deprecated 方法名已过时，请使用getHomeDir(String...)方法
     */
    public static String getHomeFullPath(String... relativeName) {
        return getHomeDir(relativeName);
    }

	/*public static String getFullPath(final String relativeName) {
		String filePath = getUserDir();
		return String.join(File.separator, filePath, relativeName);
	}*/

    /**
     * 获取用户工作路径（程序启动路径），拼接相对路径
     *
     * @return String 用户工作路径拼接相对路径后的路径
     * @deprecated 方法名已过时，请使用getUserDir(String...)方法
     */
    public static String getFullPath(String... relativeName) {
        return getUserDir(relativeName);
    }

    /**
     * 遍历java.library.path，查找本地库文件的绝对路径
     *
     * @param libFullName 本地库文件的磁盘文件名
     * @return String 本地库文件的绝对路径
     */
    public static String getAbsJniLibPath(String libFullName) {
        String jniLibPath = System.getProperty("java.library.path");
        return getAbsLibPath(jniLibPath, libFullName);
    }

    /**
     * 遍历jna.library.path，查找本地库文件的绝对路径
     *
     * @param libFullName 本地库文件的磁盘文件名
     * @return String 本地库文件的绝对路径
     */
    public static String getAbsJnaLibPath(String libFullName) {
        String jnaLibPath = System.getProperty("jna.library.path");
        return getAbsLibPath(jnaLibPath, libFullName);

    }

    private static String getAbsLibPath(String jLibPath, String libFullName) {
        if (jLibPath == null || jLibPath.isEmpty()) {
            return "";
        }
        String seperator = ":";
        int idx = jLibPath.indexOf(';');
        if (idx != -1) {
            seperator = ";";
        }
        String[] pathArr = jLibPath.split(seperator);
        for (String path : pathArr) {
            path = path.trim();
            File dir = new File(path);
            String fullFilePath;
            if (dir.isAbsolute()) {// 如果库路径是绝对路径
                fullFilePath = String.join(File.separator, path, libFullName);
            } else {// 否则是相对路径
                fullFilePath = String.join(File.separator, getFullPath(path), libFullName);
            }
            File fullFile = new File(fullFilePath);
            if (fullFile.exists()) {
                return fullFile.getAbsolutePath();
            }
        }
        return "";
    }

    /**
     * 递归清空给定文件夹下的文件及所有子目录，但不删除所给定的文件夹
     *
     * @param directory 操作的文件夹路径
     * @throws IOException IO异常
     */
    public static void cleanDirectory(File directory) throws IOException {
        if (directory.exists()) {
            org.apache.commons.io.FileUtils.cleanDirectory(directory);
        }
    }

    /**
     * 递归清空给定文件夹下的文件及所有子目录，并且删除所给定的文件夹
     *
     * @param directory 操作的文件夹路径
     * @throws IOException IO异常
     */
    public static void deleteDirectory(File directory) throws IOException {
        org.apache.commons.io.FileUtils.deleteDirectory(directory);
    }

    /**
     * 如果存在，则单纯删除非目录文件对象本身
     *
     * @param file 非目录文件对象
     */
    public static boolean deleteFile(File file) {
        if (file != null && file.exists() && file.isFile()) {
            return file.delete();
        }
        return false;
    }

    public static boolean deleteFile(String filePath) throws IOException {
        return deleteFile(new File(filePath));
    }

    /**
     * 静默删除文件或文件夹，不抛出异常。
     * 注意：如果是文件夹，清空文件夹同时删除该文件夹本身。
     *
     * @param filePathToDelete 要删除的文件或文件夹
     */
    public static void deleteQuietly(String filePathToDelete) {
        org.apache.commons.io.FileUtils.deleteQuietly(new File(filePathToDelete));
    }

    /**
     * 移动文件
     * <p>
     * 如果是跨文件系统移动，则使用“复制+删除”的方法
     *
     * @param srcFile 源文件
     * @param destDir 目标文件
     * @throws NullPointerException 如果源文件对象或目标文件对象为空 {@code null}
     * @throws FileExistsException  如果目标文件已存在
     * @throws IOException          如果源文件和目标文件无效和
     * @throws IOException          如果在移动过程中发生IO错误
     */
    public static void moveFile(File srcFile, File destDir) throws IOException {
        org.apache.commons.io.FileUtils.moveFile(srcFile, destDir);
    }

    /**
     * 移动文件（非路径）到指定路径
     *
     * @param srcFile       被移动的文件（非路径）
     * @param destDir       目标路径
     * @param createDestDir 如果目标路径不存在，是否创建
     * @throws IOException IO异常
     */
    public static void moveFileToDirectory(File srcFile, File destDir,
                                           boolean createDestDir) throws IOException {
        org.apache.commons.io.FileUtils.moveFileToDirectory(srcFile, destDir, createDestDir);
    }

    /**
     * 判断给定的文件或文件夹的路径是否存在
     *
     * @param path 文件或文件夹路径
     * @return boolean 是否存在
     */
    public static boolean fileExists(String path) {
        return new File(path).exists();
    }

    /**
     * 递归检索磁盘文件
     *
     * @param directory  检索目录起点
     * @param extensions 文件扩展名数组
     * @param recursive  是否递归（即包含子文件夹）
     * @return Collection<File> 文件集合
     */
    public static Collection<File> listFiles(File directory, String[] extensions,
                                             boolean recursive) {
        return org.apache.commons.io.FileUtils.listFiles(directory, extensions, recursive);
    }

    /**
     * 递归检索磁盘文件
     *
     * @param directory     检索目录起点
     * @param extensions    文件扩展名数组
     * @param recursive     是否递归（即包含子文件夹）
     * @param caseSensitive 扩展名是否区分大小写
     * @return Collection<File> 文件集合
     */
    public static Collection<File> listFiles(File directory, String[] extensions,
                                             boolean recursive, boolean caseSensitive) {
        if (extensions == null || extensions.length == 0) {
            throw new IllegalArgumentException("extensions must not be null or empty");
        }
        List<File> files = new ArrayList<>();
        listFiles(directory, files, Lists.asList(extensions), recursive, caseSensitive);
        return files;
    }

    private static void listFiles(File directory, List<File> fileList, List<String> extensions,
                                  boolean recursive, boolean caseSensitive) {
        List<File> subfiles = Lists.asList(directory.listFiles());
        for (File file : subfiles) {
            if (recursive && file.isDirectory()) {
                listFiles(file, fileList, extensions, recursive, caseSensitive);
            } else if (file.isFile() && validateFileName(file, extensions, caseSensitive)) {
                fileList.add(file);
            }
        }
    }

    private static boolean validateFileName(File file, List<String> extensions, boolean caseSensitive) {
        String ext = FilenameUtils.getExtension(file.getName());
        if (caseSensitive) {
            return extensions.contains(ext);
        }
        return Lists.containsIgnoreCase(extensions, ext);
    }
    /**
     * 递归检索磁盘文件
     *
     * @param directory  检索目录起点
     * @param fileFilter 文件过滤器
     * @param dirFilter  文件夹过滤器
     * @return Collection<File> 文件集合
     */
    public static Collection<File> listFiles(File directory, IOFileFilter fileFilter,
                                             IOFileFilter dirFilter) {
        return org.apache.commons.io.FileUtils.listFiles(directory, fileFilter, dirFilter);
    }

    /**
     * 检索给定目录下的目录或文件
     *
     * @param directory  检索目录起点
     * @param fileFilter 文件过滤器
     * @return Collection<File> 文件集合
     * @author hardson
     */
    public static Collection<File> listFiles(File directory, java.io.FileFilter fileFilter) {
        return Lists.asList(directory.listFiles(fileFilter));
    }

    public static void write(File file, CharSequence data, String charsetName)
            throws IOException {
        org.apache.commons.io.FileUtils.write(file, data, charsetName);
    }

    public static void write(File file, CharSequence data, Charset charset)
            throws IOException {
        org.apache.commons.io.FileUtils.write(file, data, charset);
    }

    public static void writeByteArrayToFile(String filePath, byte[] data) throws IOException {
        writeByteArrayToFile(new File(filePath), data);
    }

    public static void writeByteArrayToFile(File file, byte[] data) throws IOException {
        org.apache.commons.io.FileUtils.writeByteArrayToFile(file, data);
    }

    /**
     * 将java对象持久化到文件中。<br/>
     * 如果文件对象是路径或文件夹，抛出IllegalArgumentException异常<br/>
     * 如果文件不存在，创建文件路径和文件。<br/>
     * 如果文件不可写或设置可写失败，抛出IOException异常。
     *
     * @param <T>    T范型
     * @param file   目标文件
     * @param object T类型的java对象
     * @throws IOException IO异常
     */
    public static <T> void writeObject(File file, T object) throws IOException {
        if (file.isDirectory()) {
            throw new IllegalArgumentException("保存对象的文件对象是路径：" + file.getCanonicalPath());
        }
        if (!file.exists()) {
            forceMkdirParent(file);
            boolean flag = file.createNewFile();
        }
        if (!file.canWrite() && !file.setWritable(true)) {
            throw new IOException("文件不可写：" + file.getCanonicalPath());
        }
        try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(file.toPath()))) {
            oos.writeObject(object);
        }
    }

    /**
     * 从文件中读取持久化java对象<br/>
     * 如果文件对象是路径或文件夹，抛出IllegalArgumentException异常<br/>
     * 如果文件不可读或设置可读失败，抛出IOException异常。
     *
     * @param <T>  T类型返回值
     * @param file 存储java对象的文件对象
     * @return java对象
     * @throws IOException            IO异常
     * @throws ClassNotFoundException 类型未找到异常
     */
    public static <T> T readObject(File file) throws IOException, ClassNotFoundException {
        if (file.isDirectory()) {
            throw new IllegalArgumentException("读取对象的文件对象是路径：" + file.getCanonicalPath());
        }
        if (!file.canRead() && !file.setReadable(true)) {
            throw new IOException("文件不可读：" + file.getCanonicalPath());
        }
        try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(file.toPath()))) {
            @SuppressWarnings("unchecked")
            T obj = (T) ois.readObject();
            return obj;
        }
    }

    public static String readFileToString(File file, String charsetName) throws IOException {
        return readFileToString(file, Charsets.toCharset(charsetName));
    }

    public static String readFileToString(File file, Charset charset) throws IOException {
        return org.apache.commons.io.FileUtils.readFileToString(file, charset);
    }

    public static byte[] readFileToByteArray(String filePath) throws IOException {
        return readFileToByteArray(new File(filePath));
    }

    public static byte[] readFileToByteArray(File file) throws IOException {
        return org.apache.commons.io.FileUtils.readFileToByteArray(file);
    }

    public static void forceMkdirParent(File file) throws IOException {
        org.apache.commons.io.FileUtils.forceMkdirParent(file);
    }

    public static void forceMkdir(File file) throws IOException {
        org.apache.commons.io.FileUtils.forceMkdir(file);
    }

    public static void copyFile(File srcFile, File destFile) throws IOException {
        org.apache.commons.io.FileUtils.copyFile(srcFile, destFile);
    }

    public static void copyFile(String srcFilePath, String destFIlePath) throws IOException {
        org.apache.commons.io.FileUtils.copyFile(new File(srcFilePath), new File(destFIlePath));
    }
}
